﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using ComLib.ConfigService;
using System.Drawing;
using ComLib.ComType;
using LogLib;
using ComLib.ComFun;
using System.Drawing.Imaging;
using System.IO;
using ProcessConfig.ConfigType;
using ProcessConfig;
using System.Diagnostics;
using PixelDetectSDKInterface;
using System.Runtime.InteropServices;

namespace DetectProcessing
{
    /// <summary>
    /// 持有像素句柄的动作执行者
    /// </summary>
    public class PxlHandler
    {
        private static PixelDetectFactory factory = PixelDetectFactory.Instance;

        protected _FGImageDetectStdData _stdData = new _FGImageDetectStdData();
        /// <summary>
        /// 错误信息
        /// </summary>
        protected string _error = "";
        public string ErrorMessage
        {
            private set { _error = value; }
            get { return _error; }
        }

        protected FrontDiagnose _diagFront = new FrontDiagnose();

        protected Stopwatch _sw = new Stopwatch();

        /// <summary>
        /// PXL处理选项
        /// </summary>
        protected PxlAdjustOption _adjustOption = new PxlAdjustOption();

        /// <summary>
        /// 照片应用选项
        /// </summary>
        protected PhotoApplyOption _applyOption = new PhotoApplyOption();
       
        /// <summary>
        /// 文件的唯一名称
        /// </summary>
        protected string _uniqueFileName;
        public string UniqueFileName
        {
            get { return _uniqueFileName; }
            set { _uniqueFileName = value; }
        }

        protected IntPtr _handler = IntPtr.Zero;

        /// <summary>
        /// 当前照片在连拍过程中的索引
        /// </summary>
        private int _index = -1;
        public int Index
        {
            get { return _index; }
            set { _index = value; }
        }

        public PxlHandler(PxlAdjustOption option, PhotoApplyOption optApp)
        {
            _adjustOption = option;
            _applyOption = optApp;
            _handler = factory[_applyOption.ApplyType];
            _stdData = factory.GetStdData(_handler);
        }

        /// <summary>
        /// 执行识别算法, 识别面部特征, 为正式拍照做准备
        /// </summary>
        /// <param name="srcIn"></param>
        /// <param name="detecRes"></param>
        /// <returns>脸部检测是否成功</returns>
        public bool DetectFacePosition(RecogIn srcIn, RecogOut detecRes, bool detectFront = false)
        {
            detecRes.PassElems.Reset();
            detecRes.Computation.Eyes.Clear();
            byte[] byteImg = srcIn.CheckImageData;
            if (byteImg == null || byteImg.Length == 0)
            {
                Log.WriteAsync("The detect image is null.");
                return false;
            }
            try
            {
                _FGFaceLocationAttribute faceLocation = new _FGFaceLocationAttribute();
                DateTime dtStart = LogConfig.Now();
                //快速识别人脸和人眼
                int ret = PixelDetectSDK.DetectFaceLocation(_handler, byteImg, byteImg.Length, out faceLocation);
                if (ret != 0
                    || faceLocation.face_width == 0
                    || faceLocation.face_left == 0
                    || faceLocation.head_left == 0)
                {
                    if (ret == (int)PixelError.PIXEL_NOFACE)
                    {
                        detecRes.Correction.Tips = "检测Face数据为空";
                    }
                    else
                    {
                        detecRes.Correction.Tips = "识别FaceLocation失败:" + ret.ToString();
                    }
                    Log.WriteAsync(detecRes.Correction.Tips);
                    byteImg = null;
                    return false;
                }

                detecRes.Computation.FacePoint = ToFGFacePoint(faceLocation);
              
                Rectangle face = new Rectangle(
                    faceLocation.head_left,
                    faceLocation.head_top,
                    faceLocation.head_left - faceLocation.head_right,
                    faceLocation.chin_pos - faceLocation.head_top);
                detecRes.Computation.FaceRec = face;

                //眼睛赋值
                Rectangle eye1 = new Rectangle(faceLocation.eye_leftX - 5, faceLocation.eye_leftY - 5, 10, 10);
                detecRes.Computation.Eyes.Add(eye1);
                Rectangle eye2 = new Rectangle(faceLocation.eye_rightX - 5, faceLocation.eye_rightY - 5, 10, 10);
                detecRes.Computation.Eyes.Add(eye2);
                detecRes.Correction.ShoulderLine = (int)faceLocation.shoulder_tilt; // -1右肩高，1左肩高，0正常             
                detecRes.PassElems.FaceEye = (!detecRes.Computation.IsEyesEmpty());
                if (detectFront)
                {
                    HandleFacePose(byteImg, detecRes);
                    _diagFront.Handle(detecRes);
                }
                byteImg = null;
                return true;
            }
            catch (System.Exception e)
            {
                byteImg = null;
                Log.WriteException(e.ToString());
                return false;
            }
        }

        public static FGFacePoint ToFGFacePoint(_FGFaceLocationAttribute value)
        {
            FGFacePoint p = new FGFacePoint();
            p.chinPos = value.chin_pos;
            p.faceLeft = value.face_left;
            p.faceRight = value.face_right;
            //p.faceType = value.face_type;
            p.headLeft = value.head_left;
            p.headRight = value.head_right;
            p.headTop = value.head_top;
            //p.skewAngle = value.skew_angle;
            p.xleft = value.eye_leftX;
            p.xright = value.eye_rightX;
            p.yleft = value.eye_leftY;
            p.yright = value.eye_rightY;
            return p;
        }

        /// <summary>
        /// 检测脸部姿态是否摆正（侧转，头歪，俯仰等）
        /// </summary>
        /// <param name="byteImg"></param>
        /// <param name="detecRes"></param>
        private void HandleFacePose(byte[] byteImg, RecogOut detecRes)
        {
            _FGFaceAttribute faceAttr = new _FGFaceAttribute();
            // 快速检测图像品质               
            int ret = PixelDetectSDK.DetectFacePose(_handler, byteImg, byteImg.Length, out faceAttr);
            if (ret != 0)
            {
                Log.WriteAsync("检测人脸姿态失败:" + ret.ToString());
                return;
            }

            // 脸旋转-1：脸逆时针转，1：脸顺时针转，0:没转 
            if (faceAttr.roll < _stdData.ffroll1)
            {
                detecRes.Correction.Faceroll = -1;
            }
            else if(faceAttr.roll >= _stdData.ffroll1 && faceAttr.roll <= _stdData.ffroll2)
            {
                detecRes.Correction.Faceroll = 0;
            }
            else if(faceAttr.roll > _stdData.ffroll2)
            {
                detecRes.Correction.Faceroll = 1;
            }

            // 脸侧转-1：脸向左侧转，1：脸向右侧转，0:没转
            if (faceAttr.yaw < _stdData.ffyaw1)
            {
                detecRes.Correction.Faceyaw = -1;
            }
            else if (faceAttr.yaw >= _stdData.ffyaw1 && faceAttr.yaw <= _stdData.ffyaw2)
            {
                detecRes.Correction.Faceyaw = 0;
            }
            else if (faceAttr.yaw > _stdData.ffyaw2)
            {
                detecRes.Correction.Faceyaw = 1;
            }               

            // 头俯昂-1：头向下俯,   1：头向上昂，  0:不俯昂   
            if (faceAttr.pitch < _stdData.fhpitch1)
            {
                detecRes.Correction.Headpitch = -1;
            }
            else if (faceAttr.pitch >= _stdData.fhpitch1 && faceAttr.pitch <= _stdData.fhpitch2)
            {
                detecRes.Correction.Headpitch = 0;
            }
            else if (faceAttr.pitch > _stdData.fhpitch2)
            {
                detecRes.Correction.Headpitch = 1;
            }                   
        }

        /// <summary>
        /// 保存原图数据
        /// </summary>
        /// <param name="recogIn"></param>
        /// <param name="detecRes"></param>
        /// <returns></returns>
        public void SaveSourceData(RecogIn recogIn, RecogOut detecRes)
        {
            _uniqueFileName = recogIn.FileName;
            if (_index >= 0 && _index < Configs.TakingOption.TakingCount)
            {
                detecRes.SourceImagePath = string.Format(@"{0}\{1}_{2}{3}",
                    Configs.PhoDirs.Orig,
                    _uniqueFileName,
                    _index + 1,
                    GetFileSuffix(_applyOption.SourceImageFormat));
            }
            else
            {
                detecRes.SourceImagePath = string.Format(@"{0}\{1}{2}",
                   Configs.PhoDirs.Orig,
                   _uniqueFileName,
                   GetFileSuffix(_applyOption.SourceImageFormat));
            }
            try
            {
                recogIn.SaveSourceBitmap(detecRes.SourceImagePath, IPCCom.GetImageFormat(_applyOption.SourceImageFormat));
                Log.WriteAsync("原图已经保存到: " + detecRes.SourceImagePath);
            }
            catch (Exception ex)
            {
                _error = "保存原图失败:" + ex.Message;
                Log.WriteException(ex.ToString());
            }                     
        }

        protected string GetFileSuffix(ImageFormats format)
        {
            switch (format)
            {
                case ImageFormats.Bmp:
                    return ".bmp";
                case ImageFormats.Png:
                    return ".png";
                case ImageFormats.Jpeg:
                    return ".jpg";
                default:
                    return ".jpg";
            }          
        }

        protected bool SaveCropImage(object vData, RecogOut detecRes)
        {
            if (_index >= 0)
            {
                detecRes.DestImagePath = string.Format(@"{0}\{1}_{2}{3}",
                    Configs.PhoDirs.Crop,
                    _uniqueFileName,
                    _index + 1,
                    GetFileSuffix(_applyOption.DestImageFormat));
            }
            else
            {
                detecRes.DestImagePath = string.Format("{0}\\{1}{2}",
                    Configs.PhoDirs.Crop,
                    _uniqueFileName,
                    GetFileSuffix(_applyOption.DestImageFormat));
            }
            MemoryStream msTmp = new MemoryStream((byte[])vData); ;
            Bitmap cropImg = new Bitmap(msTmp);
            try
            {
                cropImg.Save(detecRes.DestImagePath, IPCCom.GetImageFormat(_applyOption.DestImageFormat));              
                return true;
            }
            catch (Exception ex)
            {
                _error = "保存CROP照片失败!";
                Log.WriteException(ex.ToString());
                return false;
            }
            finally
            {
                msTmp.Close();
                msTmp.Dispose();
                msTmp = null;
                cropImg.Dispose();
                cropImg = null;
                vData = null;
            }
        }

        public bool PrepareForCroping(RecogIn srcIn, RecogOut detecRes)
        {
            DetectFacePosition(srcIn, detecRes);
            if (!detecRes.PassElems.FaceEye)
            {
                Log.WriteAsync("PXL检测结果不通过: 眼睛和面部识别失败");
                return false;
            }
            return true;
        }

        /// <summary>
        ///  使用像素公司的接口对原图像进行裁切, 颜色调整 和 去背景
        /// </summary>
        /// <param name="srcIn">输入图像</param>
        /// <param name="detecRes">输出检测结果</param>    
        /// <param name="index">连拍索引记录</param>
        /// <returns></returns>
        public bool CropAndAdjust(RecogIn srcIn, RecogOut detecRes, int index = -1)
        {

            Action<string, string> OnDetectFailedHandler = (stepInfo, errorInfo) =>
            {
                detecRes.UnAcceptItem = UnAcceptableItem.Crop;
                detecRes.Correction.Tips = stepInfo + errorInfo;
                Log.WriteAsync(detecRes.Correction.Tips);
            };

            _index = index;

            int ret = DetectSDKInterface.LoadImage(_handler, srcIn.SourceImageData, srcIn.SourceDataLength);
            if (0 != ret)
            {                
                OnDetectFailedHandler("LoadImage失败", DetectHandleResult.ParseErrorMessage(ret));
                return true;
            }
            Log.WriteAsync("AdjustImage start...");
            ret = DetectSDKInterface.AdjustImage(_handler,  _adjustOption.AdjustFlag);//裁切
            Log.WriteAsync("AdjustImage end...");
            if (ret != 0)
            {
                OnDetectFailedHandler("AdjustImage失败", DetectHandleResult.ParseErrorMessage(ret));
                return false;
            }
            return Save(detecRes);
        }

        private bool Save(RecogOut detecRes)
        {
            Action<string, string> OnDetectFailedHandler = (stepInfo, errorInfo) =>
            {
                detecRes.UnAcceptItem = UnAcceptableItem.Crop;
                detecRes.Correction.Tips = stepInfo + errorInfo;
                Log.WriteAsync(detecRes.Correction.Tips);
            };

            int size = Marshal.SizeOf(typeof(int));
            IntPtr ptr = Marshal.AllocHGlobal(size);
            int ret = DetectSDKInterface.GetImageSize(_handler, ptr, ".jpg");
            int length = Marshal.ReadInt32(ptr);
            Marshal.FreeHGlobal(ptr);
            if (ret != 0)
            {
                OnDetectFailedHandler("GetImageSize失败:", DetectHandleResult.ParseErrorMessage(ret));
                return false;
            }
            if (length == 0)
            {
                OnDetectFailedHandler("GetImageSize失败:", DetectHandleResult.ParseErrorMessage(ret));
                return false;
            }
            byte[] destDataArray = new byte[length];
            ret = DetectSDKInterface.GetImage(_handler, destDataArray, ".jpg");
            if (ret != 0)
            {
                OnDetectFailedHandler("GetImage:", DetectHandleResult.ParseErrorMessage(ret));
                return false;
            }

            if (_index >= 0)
            {
                detecRes.DestImagePath = string.Format(@"{0}\{1}_{2}{3}",
                    Configs.PhoDirs.Crop,
                    _uniqueFileName,
                    _index + 1,
                    GetFileSuffix(_applyOption.DestImageFormat));
            }
            else
            {
                detecRes.DestImagePath = string.Format("{0}\\{1}{2}",
                    Configs.PhoDirs.Crop,
                    _uniqueFileName,
                    GetFileSuffix(_applyOption.DestImageFormat));
            }

            MemoryStream msTmp = new MemoryStream(destDataArray);
            Bitmap cropImg = new Bitmap(msTmp);
            try
            {
                cropImg.Save(detecRes.DestImagePath, ImageFormat.Jpeg);
                return true;
            }
            catch (Exception ex)
            {
                Log.WriteException(ex.ToString());
                return false;
            }
            finally
            {
                msTmp.Close();
                msTmp.Dispose();
                msTmp = null;
                cropImg.Dispose();
                cropImg = null;
                destDataArray = null;
            }
        }

    }
}
